package com.ck.web.shiro;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

/**
 * 实现Shiro基于Form表单的身份验证过滤器，用于用户登录<br/>
 * 1.扩展org.apache.shiro.web.filter.authc.FormAuthenticationFilter的属性，增加验证码参数<br/>
 * 2.该类的实例对象会在spring-context-shiro.xml配置文件中注册，通过Spring容器可以获取该类对象<br/>
 * 3.在Spring容器中注册时需指定loginUrl、usernameParam、passwordParam、captchaParam、rememberMeParam属性<br/>
 * 作者：ChengK <br/>
 * 创建时间：2016/11/8 21:36 <br/>
 */
@Slf4j
@Getter
@Setter
@ToString
public class LoginFormAuthenticationFilter extends FormAuthenticationFilter {

    /**
     * 用户登录表单的验证码参数名，Spring容器注册时需要指定该属性的值
     */
    private String captchaParam = "validateCode";

    /**
     * 处理登录表单中的参数，根据登录表单的参数生成 AuthenticationToken<br>
     *
     * @param request  htttp请求参数
     * @param response htpp响应参数
     * @return 返回用户提交的身份信息Toke，UserAuthorizingRealm 会使用此Toke验证用户是否合法
     */
    @Override
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response){
        log.debug("==========================进入SHIRO createToken=================================");

        UserLoginToken userLoginToken;
        // 获取用户登录的验证信息
        String userName = getUsername(request);
        String password = getPassword(request);
        boolean rememberMe = this.isRememberMe(request);
        String host = this.getHost(request);
        String captcha = request.getParameter(this.getCaptchaParam());
        if (password == null) {
            password = "";
        }
        // 根据用户验证信息构造Shiro验证Token
        userLoginToken = new UserLoginToken(userName, password.toCharArray(), rememberMe, host, captcha);
        log.debug("用户登录请求Toke  " + userLoginToken.toString());
        return userLoginToken;
    }

    /**
     * 用户登录认证方法，当用户登录时调用
     */
    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response){
        AuthenticationToken token = createToken(request, response);
        try {
            if(token == null){
                throw new LoginException(LoginExceptionEnum.SYSTEM_EXCEPTION.getCode(),"获取token失败");
            }

            UserLoginToken userToken = (UserLoginToken) token;
            log.debug("用户[{}]登录身份验证开始...", userToken.getUsername());

            // 用户登录认证，参考：super.executeLogin(request, response)
            if (!(request instanceof HttpServletRequest)) {
                throw new LoginException(LoginExceptionEnum.SYSTEM_EXCEPTION.getCode(), "ServletRequest转换HttpServletRequest失败");
            }

            Subject subject = getSubject(request, response);
            subject.login(token);
            return onLoginSuccess(token, subject, request, response);
        } catch (Exception e) {
            log.error("用户登录认证-异常", e);
            AuthenticationException exception;
            if(e instanceof AuthenticationException){
                exception = (AuthenticationException) e;
            }else{
                exception = new AuthenticationException(e);
            }
            return onLoginFailure(token, exception, request, response);
        }
    }

    /**
     * 登录失败回调方法
     * @param token 登录token
     * @param e 异常信息
     */
    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
        return super.onLoginFailure(token, e, request, response);
    }

    /**
     * 登录成功回调方法
     */
    @Override
    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
        return super.onLoginSuccess(token, subject, request, response);
    }

    /**
     * 登录成功后跳转
     */
    @Override
    protected void issueSuccessRedirect(ServletRequest request, ServletResponse response) throws Exception {
        super.issueSuccessRedirect(request, response);
    }

    /**
     * 设置登录失败信息<br>
     *
     * @param request 请求对象
     * @param ae      登录失败异常
     */
    @Override
    protected void setFailureAttribute(ServletRequest request, AuthenticationException ae) {
        request.setAttribute(getFailureKeyAttribute(), ae);
    }

}
